package cn.tedu.charging.device.warm;

import cn.tedu.charging.common.pojo.po.ChargingStationPO;
import cn.tedu.charging.device.mapper.StationMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Slf4j
public class StationWarmUp implements ApplicationRunner {
    public static final String ALL_STATIONS="charging:stations:all";
    @Autowired
    private StationMapper stationMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    /*VERSION2*/
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("预热开始执行");
        //1.判断是否是第一次读写
        //1.1exists charging:stations:all 返回值是1 结束 0 就继续
        Boolean exists = redisTemplate.hasKey(ALL_STATIONS);
        if (!exists){
            log.info("预热数据不存在,正常执行预热");
            //2.定义batch10000 查询total 计算n 循环 每次循环 读取一部分数据 写入redis
            Integer batch=10000;
            Long total=getStationsCount();
            if(total<=batch){
                //执行一批 读取和写入
                getStationsAndSaveGeoOnce(batch);
            }else{
                //计算循环次数 n=total%batch==0?total/batch:total/batch+1
                long n=(total%batch==0?(total/batch):(total/batch+1));
                for (long i=0;i<n;i++){
                    //么次循环 都是需要查一批数据 start=i*batch size=batch
                    getStationsAndSaveGeo(i*batch,batch);
                }
            }
        }else{
            log.info("预热数据已存在,预热结束");
        }



    }

    private void getStationsAndSaveGeoOnce(Integer batch) {
        //select * from charging_station limit 0,10000
        this.getStationsAndSaveGeo(0,batch);
    }

    private void getStationsAndSaveGeo(long start, Integer size) {
        //select * from charging_stations limit #{start},#{size}
        //1.查询数据库 本批次分页数据
        QueryWrapper queryWrapper=new QueryWrapper();
        //分页条件 可以放到queryWrapper
        queryWrapper.last("limit "+start+","+size);
        List<ChargingStationPO> pos = stationMapper.selectList(queryWrapper);
        //TODO 对pos做循环 调用geoadd 性能低
        //geoadd支持一次性写入多条 10000条对redis来说 小意思
        //pos转化成geoLocation集合
        List<RedisGeoCommands.GeoLocation<Integer>> geos=new ArrayList<>();
        //使用list的stream()的map映射
        for (ChargingStationPO po : pos) {
            double lng = po.getStationLng().doubleValue();
            double lat = po.getStationLat().doubleValue();
            Integer stationId = po.getId();
            RedisGeoCommands.GeoLocation<Integer> location=
                    new RedisGeoCommands.GeoLocation<>(stationId,new Point(lng,lat));
            geos.add(location);
        }
        /*stream() 集合流api
        geos=pos.stream().map(po->{
            //po转成geoLocation 集合映射计算算法
            double lng = po.getStationLng().doubleValue();
            double lat = po.getStationLat().doubleValue();
            Integer stationId = po.getId();
            RedisGeoCommands.GeoLocation<Integer> location=
                    new RedisGeoCommands.GeoLocation<>(stationId,new Point(lng,lat));
            return location;
        }).collect(Collectors.toList());*/
        redisTemplate.opsForGeo().add(ALL_STATIONS,geos);
    }

    private Long getStationsCount() {
        //select count(*) from charging_station
        return stationMapper.selectCount(null);//6
    }
    /**
     * bean生命周期 伴随容器启动 流转周期
     * 1. 实例化对象
     * 2. 初始化方法
     * 3. 赋值注入属性 对象
     * 4. 结束初始化
     * 5. 在容器驻留
     * 6. 实现接口后 会在容器最终成功运行启动后 调用run
     */
    /*VERSION1实现*/
    /*@Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("预热开始执行");
        //1.读取所有数据
        //select * from charging_station
        List<ChargingStationPO> stationPOs = stationMapper.selectList(null);
        //2.将数据库中读取的po 写入到redis里
        GeoOperations<String,Integer> geoOps = redisTemplate.opsForGeo();
        for (ChargingStationPO po : stationPOs) {
            //geoadd charging:stations:all po.getLng po.getLat po.getId
            geoOps.add(ALL_STATIONS,
                    new Point(
                            po.getStationLng().doubleValue(),
                            po.getStationLat().doubleValue()
                    ),po.getId());
        }
    }*/
}
